package com.ruoyi.aitcommon.aspectj;

import java.util.Date;
import java.util.concurrent.locks.Lock;

import com.ruoyi.aitcommon.config.AITConfig;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.aitcommon.anotation.AitFlow;
import com.ruoyi.aitcommon.config.ConfigManager;
import com.ruoyi.aitcommon.domain.AitLog;
import com.ruoyi.aitcommon.service.IAitLogService;
import com.ruoyi.aitcommon.utils.AspectjUtils;
import com.ruoyi.aitcommon.utils.FlowUtils;
import com.ruoyi.aitcommon.utils.LockUtils;
import com.ruoyi.aitcommon.utils.LogUtils;
import com.ruoyi.aitcommon.utils.RuntimeManager;
import com.ruoyi.aitcommon.vo.FlowContextVO;

import cn.hutool.core.util.StrUtil;

@Aspect
@Component
public class AitFlowAspect {
	@Autowired
	private IAitLogService logService;
	@Autowired
	ConfigManager configManager;
	@Autowired
	RuntimeManager runtimeManager;

	// 配置织入点
	@Pointcut("@annotation(com.ruoyi.aitcommon.anotation.AitFlow)")
	public void pointCut() {
	}

	@Around("pointCut()")
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
//		上层flow上下文 暂存
		FlowContextVO pContext=null;
		// 获得注解
		AitFlow ann = AspectjUtils.getAnnotation(point, AitFlow.class);
		try {
			if (ann == null) {
				return null;
			}
//            获取参数
			Object[] args = AspectjUtils.getParmValues(point);
//			备份上层flow上下文
			if(FlowUtils.getFlowContext()!=null) {
				pContext=FlowUtils.getFlowContext();
			}
//        	创建上下文
			createContext(args);

//			记录运行状态,这个要在创建上下文之后
			runtimeManager.openAITInfo();

//			执行前上锁
//			按照FlowCode上锁,不同Flow可以并发执行,相同Flow并发,直接退出
			Lock lock = LockUtils.TryLock(FlowUtils.getFlowCode());
			if(lock==null) {
//				获取锁失败,程序不在执行
				LogUtils.error("流程获取锁失败:FLowID:[{}] FlowCode:[{}] FlowName:[{}],排队数:[{}]", FlowUtils.getFlowID(),
						FlowUtils.getFlowCode(), FlowUtils.getFlowName(),LockUtils.getReqNum(FlowUtils.getFlowCode()));
				return null;
			}
			
//        	记录日志
			LogUtils.info("~~~~~~~~~~~~~~~~流程日志 编码:[{}] 名称:[{}] ID:[{}]开始~~~~~~~~~~~~~~~~~~~~~~~~",
					FlowUtils.getFlowCode(), FlowUtils.getFlowName(), FlowUtils.getFlowID());
			FlowUtils.addBulletin("流程开始: FLowID:[{}] FlowCode:[{}] FlowName:[{}]", FlowUtils.getFlowID(),
					FlowUtils.getFlowCode(), FlowUtils.getFlowName());

//          执行目标方法
			Object returnValue = point.proceed(args);

//          执行后返回
//          记录日志
			LogUtils.info("~~~~~~~~~~~~~~~~流程日志 编码:[{}] 名称:[{}] ID:[{}]结束~~~~~~~~~~~~~~~~~~~~~~~~",
					FlowUtils.getFlowCode(), FlowUtils.getFlowName(), FlowUtils.getFlowID());
//			LogUtils.info("流程日志-运行时间：{}毫秒", FlowUtils.getTimeConsume());
//			LogUtils.info("流程日志-处理记录数:成功{}条,失败{}条", FlowUtils.getSuccessCnt(), FlowUtils.getErrorCnt());
			FlowUtils.addBulletin("总体运行时间:[{}]毫秒", FlowUtils.getTimeConsume());
			FlowUtils.addBulletin("处理记录数:成功[{}]条,失败[{}]条", FlowUtils.getSuccessCnt(), FlowUtils.getErrorCnt());
//			记录运行状态
			runtimeManager.closeAITInfo();

			return returnValue;
		} catch (Exception ex) {
//    		记录本地异常日志
//    		正常情况,异常在服务切面中处理了,flow方法中不再抛异常了
//    		这里处理的切面本身报错
			LogUtils.error("AitFLow切面异常!", ex);
			throw new Exception(ex);
		} finally {
			
//			执行后,取消锁
			LockUtils.unLock(FlowUtils.getFlowCode());
			
//    		整体流程结束,需要清除流程上下文
			String bulletinStr = FlowUtils.getBulletinStr();
			LogUtils.info(bulletinStr);
//    			DB日志处理
			dbLog(point, ann);
//    			因为线程池存在,该线程可能不会释放,需要手工释放
			if(pContext==null) {
				FlowUtils.clearFlowContext();
			}else {
//				如果存在上层flow,还原上层flow上下文
//				FlowContextVO vo=pContext.toJavaObject(FlowContextVO.class);
				FlowUtils.setFlowContext(pContext);
			}
			LogUtils.info("运行路径:{}", AITConfig.getUserDir());
		}
	}

	/**
	 * 创建Flow Context
	 * 设置Flow上下文中的code,name
	 * @param args 调用args
	 */
	private void createContext(Object[] args) throws Exception {
		if (args.length > 0 && StrUtil.isBlankIfStr(args[0])) {
			throw new Exception("流程编码为空");
		}
		String flowCode = args[0].toString();
//		依据flowcode,获取flow配置文件
		Object obj = configManager.getContextByParm(flowCode);
		if (!(obj instanceof JSONObject)) {
			String msg = "流程编码[%s]对应参数不是JSON对象!";
			msg = String.format(msg, flowCode);
			throw new Exception(msg);
		}
//		设置Flow上下文中的编码/名称
		JSONObject flowInfo = (JSONObject) obj;
		String flowName = flowInfo.getString("name");
		FlowUtils.setFlowContext(flowCode, flowName);
	}

	/**
	 * 插入DB日志
	 * 
	 * @param point 切入点
	 * @param ano   注解
	 */
	private void dbLog(ProceedingJoinPoint point, AitFlow ano) {
		AitLog aitlog = new AitLog();
		// Flow信息
		aitlog.setLogType("流程日志");
		aitlog.setFlowId(FlowUtils.getFlowID());
		aitlog.setFlowCode(FlowUtils.getFlowCode());
		aitlog.setFlowName(FlowUtils.getFlowName());
		aitlog.setStartDate(FlowUtils.getStartDate());
		aitlog.setEndDate(new Date());
		aitlog.setRunTime(FlowUtils.getTimeConsume());
		// 方法信息
		aitlog.setApiName(ano.value());
		String className = point.getTarget().getClass().getName();
		String methodName = point.getSignature().getName();
		aitlog.setApiMethod(className + "." + methodName + "()");
		aitlog.setRetcode(FlowUtils.getRetCode());
		aitlog.setRetmsg(FlowUtils.getBulletinStr());
		logService.insertAitLog(aitlog);
	}
}
